package com.shitou.shitoucommons.interceptor;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.shitou.shitoucommons.annotation.AccessLimit;
import com.shitou.shitoucommons.annotation.CheckUserLogin;
import com.shitou.shitoucommons.annotation.MemberAuthorith;
import com.shitou.shitoucommons.exception.RRException;
import com.shitou.shitoucommons.opfeign.MemberFeign;
import com.shitou.shitoucommons.utils.IPUtils;
import com.shitou.shitoucommons.utils.RespBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.method.HandlerMethod;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * class detail:
 *
 * @author 石头人
 */
@SuppressWarnings("all")
@Slf4j
public class LoginInterceptor implements CheckMemberInterceptor {

    @Autowired
    @Qualifier("RedisTemplate")
    private RedisTemplate redisTemplate;


    /**
     * 接口限流校验
     * @param handler
     * @param request
     * @param response
     * @param memberFeign
     * @return
     */
    public boolean checkCurrentLimiting(Object handler, HttpServletRequest request, HttpServletResponse response, MemberFeign memberFeign) throws IOException {
        if ( handler instanceof HandlerMethod){
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            AccessLimit accessLimit = handlerMethod.getMethodAnnotation(AccessLimit.class);
            if ( accessLimit == null ){
                return true;
            }

            // 获取注解的信息
            int second = accessLimit.second();
            int maxCount = accessLimit.maxCount();
            boolean needLogin = accessLimit.needLogin();

            String key = request.getRequestURL().toString();
            // 如果已经登录了，那么就使用token进行key，如果没有登录，那么就使用ip进行key
            if ( needLogin ){
                boolean isLogin = checkLogin(handler, request, response, memberFeign);
                // 没登陆则拦截
                if ( !isLogin ){
                    return false;
                }
                Cookie[] cookies = request.getCookies();  // 能登陆，那么cookie就有值
                String token = Arrays.stream(cookies).filter(cookie -> cookie.getName().equals("memberTicket"))
                        .findFirst()
                        .map(Cookie::getValue)
                        .orElse("");
                if ( token.equals("") ){
                    return false;
                }
                key += ":" + token;
            }else {
                String ipAddress = IPUtils.getIpAddress(request);
                key += ":" + ipAddress;
            }

            Integer count = (Integer) redisTemplate.opsForValue().get(key);
            if (count == null) {
                // 如果该用户是第一次访问，那么存入redis缓存中
                redisTemplate.opsForValue().set(key, 1, second, TimeUnit.SECONDS);
            } else if (count < maxCount) {
                // 如果当前访问次数小于 在规定时间内访问的最大次数，那么redis中的访问次数自增 1
                redisTemplate.opsForValue().increment(key);
            } else {
                // 当前在一定时间内请求次数已经超过了maxCount 进行接口拦截
                render(response, RespBean.error(600, "接口访问次数太频繁"));
                return false;
            }
        }
        return true;
    }


    /**
     * 用户权限校验
     * @param handler
     * @param request
     * @param response
     */
    public boolean checkAuthority(Object handler, HttpServletRequest request, HttpServletResponse response, MemberFeign memberFeign) {

        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            MemberAuthorith methodAnnotation = handlerMethod.getMethodAnnotation(MemberAuthorith.class);
            if ( methodAnnotation == null ){
                return true;
            }

            // TODO 权限校验--远程权限校验

        }
        return true;
    }


    /**
     * 用户是否登录校验
     * @param handler
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    public boolean checkLogin(Object handler, HttpServletRequest request, HttpServletResponse response, MemberFeign memberFeign)  {
        if ( handler instanceof HandlerMethod ){
            HandlerMethod method = (HandlerMethod) handler;
            CheckUserLogin methodAnnotation = method.getMethodAnnotation(CheckUserLogin.class);
            if ( methodAnnotation == null ){
                return true;
            }
            boolean value = methodAnnotation.value();
            if ( !value ){
                return true;
            }
            Cookie[] cookies = request.getCookies();
            if ( cookies == null || cookies.length == 0 ){
                render(response, RespBean.error(402, "用户未登录~~"));
                return false;
            }
            String token = Arrays.stream(cookies)
                    .filter(cookie -> cookie.getName().equals("memberTicket"))
                    .findFirst()
                    .map(Cookie::getValue)
                    .orElse("");
            RespBean respBean = memberFeign.checkLogin(token);
            if ( respBean == null ){
                render(response, RespBean.error(402, "用户未登录~~"));
                return false;
            }
            String data = (String) respBean.get("data");
            boolean isLogin = Boolean.parseBoolean(data);

            if ( !isLogin ){
                render(response, RespBean.error(402, "用户未登录~~~"));
                return false;
            }
        }
        return true;
    }


    /**
     * 功能描述: 将信息渲染到响应体中，并返回
     * @param response
     * @param sessionError RespBeanEnum枚举类
     * @throws IOException
     */
    public void render(HttpServletResponse response, RespBean errorInfo)  {
        try {
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");
            PrintWriter printWriter = response.getWriter();
            printWriter.write(new ObjectMapper().writeValueAsString(errorInfo));
            printWriter.flush();
            printWriter.close();
        } catch (IOException e){

            e.printStackTrace();
            throw new RRException("页面渲染出错");
        }
    }
}
